/*
    控制读出电路的上电\下电过程中的各项配置
*/

#include "SchmidtCAM.h"

#define REG_NUM CCD290_DRV_CONFIG_PARAM_NUM

int socket_power_up(    SchmidtCAM_Config *cfg, 
                        uint32_t *gpio_flt_ctrl, 
                        uint32_t *gpio_flt_data,
                        uint32_t *gpio_in,
                        uint32_t *gpio_pl_ps,
                        uint32_t *gpio_out,
                        uint32_t *gpio_out2,
                        uint32_t *gpio_spi_ps_ctrl,
                        uint32_t *gpio_spi_ps_data ){

    // 1) configure AD9508
    socket_config_ad9508(   cfg, 
                            gpio_flt_ctrl, 
                            gpio_flt_data,
                            gpio_in,
                            gpio_pl_ps,
                            gpio_out,
                            gpio_out2,
                            gpio_spi_ps_ctrl,
                            gpio_spi_ps_data );


    // 2) configure PL:ADC2REG
    socket_config_adc2reg_clk(  cfg, 
                                gpio_flt_ctrl, 
                                gpio_flt_data,
                                gpio_in,
                                gpio_pl_ps,
                                gpio_out,
                                gpio_out2,
                                gpio_spi_ps_ctrl,
                                gpio_spi_ps_data );


    // 3) reset ADS5294
    socket_reset_ads5294(   cfg, 
                            gpio_flt_ctrl, 
                            gpio_flt_data,
                            gpio_in,
                            gpio_pl_ps,
                            gpio_out,
                            gpio_out2,
                            gpio_spi_ps_ctrl,
                            gpio_spi_ps_data);

    // 4) configure ADS5294
    socket_config_ads5294(  cfg, 
                            gpio_flt_ctrl, 
                            gpio_flt_data,
                            gpio_in,
                            gpio_pl_ps,
                            gpio_out,
                            gpio_out2,
                            gpio_spi_ps_ctrl,
                            gpio_spi_ps_data );


    // 5) configure CCD driver parameters
    socket_config_ccd_drive(cfg, 
                            gpio_flt_ctrl, 
                            gpio_flt_data,
                            gpio_in,
                            gpio_pl_ps,
                            gpio_out,
                            gpio_out2,
                            gpio_spi_ps_ctrl,
                            gpio_spi_ps_data );


    // 6) configure DCDS weights
    socket_config_dcds( cfg, 
                        gpio_flt_ctrl, 
                        gpio_flt_data,
                        gpio_in,
                        gpio_pl_ps,
                        gpio_out,
                        gpio_out2,
                        gpio_spi_ps_ctrl,
                        gpio_spi_ps_data );


    // 7) configure daisy chain
    socket_config_daisy(cfg, 
                        gpio_flt_ctrl, 
                        gpio_flt_data,
                        gpio_in,
                        gpio_pl_ps,
                        gpio_out,
                        gpio_out2,
                        gpio_spi_ps_ctrl,
                        gpio_spi_ps_data,
                        1 );


    // 8) configure DACx0508
    socket_config_dacx0508( cfg, 
                            gpio_flt_ctrl, 
                            gpio_flt_data,
                            gpio_in,
                            gpio_pl_ps,
                            gpio_out,
                            gpio_out2,
                            gpio_spi_ps_ctrl,
                            gpio_spi_ps_data,
                            1 );


    // 9) enable 156MHz clock
    socket_set_156MHz_clk(  cfg, 
                            gpio_flt_ctrl, 
                            gpio_flt_data,
                            gpio_in,
                            gpio_pl_ps,
                            gpio_out,
                            gpio_out2,
                            gpio_spi_ps_ctrl,
                            gpio_spi_ps_data,
                            1 );


    // 10) enable AFE
    socket_set_afe( cfg, 
                    gpio_flt_ctrl, 
                    gpio_flt_data,
                    gpio_in,
                    gpio_pl_ps,
                    gpio_out,
                    gpio_out2,
                    gpio_spi_ps_ctrl,
                    gpio_spi_ps_data,
                    1 );


    // 11) sync ads5294
    socket_sync_ads5294(    cfg, 
                            gpio_flt_ctrl, 
                            gpio_flt_data,
                            gpio_in,
                            gpio_pl_ps,
                            gpio_out,
                            gpio_out2,
                            gpio_spi_ps_ctrl,
                            gpio_spi_ps_data );

    return 0;
}

int socket_power_down(  SchmidtCAM_Config *cfg, 
                        uint32_t *gpio_flt_ctrl, 
                        uint32_t *gpio_flt_data,
                        uint32_t *gpio_in,
                        uint32_t *gpio_pl_ps,
                        uint32_t *gpio_out,
                        uint32_t *gpio_out2,
                        uint32_t *gpio_spi_ps_ctrl,
                        uint32_t *gpio_spi_ps_data ){

    // 1) disable AFE
    socket_set_afe( cfg, 
                    gpio_flt_ctrl, 
                    gpio_flt_data,
                    gpio_in,
                    gpio_pl_ps,
                    gpio_out,
                    gpio_out2,
                    gpio_spi_ps_ctrl,
                    gpio_spi_ps_data,
                    0 );


    // 2) disable 165MHz clock
    socket_set_156MHz_clk(  cfg, 
                            gpio_flt_ctrl, 
                            gpio_flt_data,
                            gpio_in,
                            gpio_pl_ps,
                            gpio_out,
                            gpio_out2,
                            gpio_spi_ps_ctrl,
                            gpio_spi_ps_data,
                            0 );

    // 3) power down DACx0508
    socket_config_dacx0508( cfg, 
                            gpio_flt_ctrl, 
                            gpio_flt_data,
                            gpio_in,
                            gpio_pl_ps,
                            gpio_out,
                            gpio_out2,
                            gpio_spi_ps_ctrl,
                            gpio_spi_ps_data,
                            0 );

    // 4) power down daisy chain
    socket_config_daisy(cfg, 
                        gpio_flt_ctrl, 
                        gpio_flt_data,
                        gpio_in,
                        gpio_pl_ps,
                        gpio_out,
                        gpio_out2,
                        gpio_spi_ps_ctrl,
                        gpio_spi_ps_data,
                        0 );

    return 0;
}

int socket_config_ad9508(   SchmidtCAM_Config *cfg, 
                            uint32_t *gpio_flt_ctrl, 
                            uint32_t *gpio_flt_data,
                            uint32_t *gpio_in,
                            uint32_t *gpio_pl_ps,
                            uint32_t *gpio_out,
                            uint32_t *gpio_out2,
                            uint32_t *gpio_spi_ps_ctrl,
                            uint32_t *gpio_spi_ps_data ){

    SPI_CONFIG *spi_config = (SPI_CONFIG*)malloc(sizeof(SPI_CONFIG));
    setup_SPI_by_device_name( spi_config, "ad9508" );

    config_ad9508_via_SPI(  gpio_out,
                            gpio_out2,
                            gpio_in,
                            spi_config,
                            cfg->config_ad9508 );

    free(spi_config);

    sleep(1);

    return 0;
}

int socket_config_adc2reg_clk(  SchmidtCAM_Config *cfg, 
                                uint32_t *gpio_flt_ctrl, 
                                uint32_t *gpio_flt_data,
                                uint32_t *gpio_in,
                                uint32_t *gpio_pl_ps,
                                uint32_t *gpio_out,
                                uint32_t *gpio_out2,
                                uint32_t *gpio_spi_ps_ctrl,
                                uint32_t *gpio_spi_ps_data ){
    
    config_adc2reg_clk_via_SPI( gpio_out, gpio_out2, cfg->config_adc2reg_clk );

    sleep(1);
    
    return 0;
}

int socket_config_ads5294(  SchmidtCAM_Config *cfg, 
                            uint32_t *gpio_flt_ctrl, 
                            uint32_t *gpio_flt_data,
                            uint32_t *gpio_in,
                            uint32_t *gpio_pl_ps,
                            uint32_t *gpio_out,
                            uint32_t *gpio_out2,
                            uint32_t *gpio_spi_ps_ctrl,
                            uint32_t *gpio_spi_ps_data ){

    SPI_CONFIG *spi_config = (SPI_CONFIG*)malloc(sizeof(SPI_CONFIG));
    setup_SPI_by_device_name( spi_config, "ads5294" );

    config_ads5294_via_SPI( gpio_out, 
                            gpio_out2, 
                            gpio_in,
                            spi_config,
                            cfg->config_ads5294 );

    free(spi_config);

    sleep(1);

    return 0;
}

int socket_config_ccd_drive(SchmidtCAM_Config *cfg, 
                            uint32_t *gpio_flt_ctrl, 
                            uint32_t *gpio_flt_data,
                            uint32_t *gpio_in,
                            uint32_t *gpio_pl_ps,
                            uint32_t *gpio_out,
                            uint32_t *gpio_out2,
                            uint32_t *gpio_spi_ps_ctrl,
                            uint32_t *gpio_spi_ps_data ){

    uint32_t reg_vals[REG_NUM] = {0};
    extract_ccd290_drive_config_params( cfg->config_ccd_drive, reg_vals );
    SPI_PS_SEND_DATA_new(   gpio_spi_ps_ctrl,
                            gpio_spi_ps_data, 
                            reg_vals, 
                            REG_NUM,
                            SPI_PS_READOUT_DRV );

    // cfg->n_row = reg_vals[0];
    // cfg->n_col = reg_vals[2];

    // printf("> n_row = %6d\n", cfg->n_row);
    // printf("> n_col = %6d\n", cfg->n_col);
    // exit(0);

    // sleep(1);

    return 0;
}

int socket_config_daisy(SchmidtCAM_Config *cfg, 
                        uint32_t *gpio_flt_ctrl, 
                        uint32_t *gpio_flt_data,
                        uint32_t *gpio_in,
                        uint32_t *gpio_pl_ps,
                        uint32_t *gpio_out,
                        uint32_t *gpio_out2,
                        uint32_t *gpio_spi_ps_ctrl,
                        uint32_t *gpio_spi_ps_data,
                        int state ){

    SPI_CONFIG *spi_config = (SPI_CONFIG*)malloc(sizeof(SPI_CONFIG));
    setup_SPI_by_device_name( spi_config, "daisy" );

    set_reg_update_option( cfg->config_daisy, gpio_out );
    config_daisy_via_SPI(   gpio_out, 
                            gpio_out2, 
                            gpio_in,
                            spi_config,
                            cfg->config_daisy,
                            state,
                            1 );

    free(spi_config);

    sleep(1);

    return 0;
}

int socket_config_dacx0508( SchmidtCAM_Config *cfg, 
                            uint32_t *gpio_flt_ctrl, 
                            uint32_t *gpio_flt_data,
                            uint32_t *gpio_in,
                            uint32_t *gpio_pl_ps,
                            uint32_t *gpio_out,
                            uint32_t *gpio_out2,
                            uint32_t *gpio_spi_ps_ctrl,
                            uint32_t *gpio_spi_ps_data,
                            int state ){

    SPI_CONFIG *spi_config = (SPI_CONFIG*)malloc(sizeof(SPI_CONFIG));
    setup_SPI_by_device_name( spi_config, "dacx0508" );
    
    config_dacx0508_via_SPI( gpio_out, 
                             gpio_out2, 
                             gpio_in,
                             spi_config,
                             cfg->config_dacx0508,
                             state );

    free(spi_config);

    sleep(1);

    return 0;
}

int socket_config_dcds( SchmidtCAM_Config *cfg, 
                        uint32_t *gpio_flt_ctrl, 
                        uint32_t *gpio_flt_data,
                        uint32_t *gpio_in,
                        uint32_t *gpio_pl_ps,
                        uint32_t *gpio_out,
                        uint32_t *gpio_out2,
                        uint32_t *gpio_spi_ps_ctrl,
                        uint32_t *gpio_spi_ps_data ){

    dictionary *dict_ini;
    dict_ini = iniparser_load( cfg->dcds_config_file );
    if( dict_ini == NULL ){
        fprintf(stderr, "cannot parse file: %s\n", cfg->dcds_config_file);
        return 0;
    }

    float scale = 1.0;

    int use_16bits_output = iniparser_getint(dict_ini, "DCDS_WEIGHTS:use_16bits_output", 0);

    if( use_16bits_output == 1 ){
        scale = 4.0;
        cfg->n_bytes = 2;
    } else{
        scale = 1.0;
        cfg->n_bytes = 4;
    }

    //  prepare DCDS weights data
    uint32_t w_size = iniparser_getint(dict_ini, "DCDS_WEIGHTS:w_size", 10);
    printf("##> DCDS weight size : %d\n", w_size);

    float regs_w_float[16];
    uint32_t *regs_w_uint[16];

    uint32_t os_ch;

    // allocate memory for DCDS weights
    for( os_ch = 0; os_ch < 16; os_ch++ ){
        regs_w_uint[os_ch]  = malloc(sizeof(uint32_t)*w_size);
    }

    char *wfile = (char*)iniparser_getstring(dict_ini, "DCDS_WEIGHTS:w_file", NULL);

    if( wfile != NULL ){
        FILE *fp = fopen(wfile, "r");

        uint32_t w_cnt = 0;

        while( fscanf(fp, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
                        &regs_w_float[0],
                        &regs_w_float[1],
                        &regs_w_float[2],
                        &regs_w_float[3],
                        &regs_w_float[4],
                        &regs_w_float[5],
                        &regs_w_float[6],
                        &regs_w_float[7],
                        &regs_w_float[8],
                        &regs_w_float[9],
                        &regs_w_float[10],
                        &regs_w_float[11],
                        &regs_w_float[12],
                        &regs_w_float[13],
                        &regs_w_float[14],
                        &regs_w_float[15] ) == 16 ){

            for( os_ch = 0; os_ch < 16; os_ch++ ){
                getFloatBin_uint32(regs_w_float[os_ch]*scale, &regs_w_uint[os_ch][w_cnt]);
            }

            w_cnt++;
        }

        printf("#==> DCDS_W_SIZE = %d\n", w_cnt);

        if( w_cnt != w_size ){
            char errmsg[1024];
            sprintf(errmsg, "w_cnt != w_size");
            printDebugInfo(errmsg);
            exit(0);
        }

        fclose(fp);
    } else {
        char errmsg[1024];
        sprintf(errmsg, "failed to get w_file from:%s", cfg->dcds_config_file);
        printDebugInfo(errmsg);
        exit(0);
    }

//  Send data to PL side:
    for( os_ch = 0; os_ch < 16; os_ch++ ){
        Config_DCDS_Weights( gpio_flt_ctrl,
                             gpio_flt_data,
                             w_size,
                             os_ch,
                             regs_w_uint[os_ch] );
    }

    for( os_ch = 0; os_ch < 16; os_ch++ ){
        free(regs_w_uint[os_ch]);
    }

    sleep(1);

    return 0;
}

int socket_reset_ads5294(   SchmidtCAM_Config *cfg, 
                            uint32_t *gpio_flt_ctrl, 
                            uint32_t *gpio_flt_data,
                            uint32_t *gpio_in,
                            uint32_t *gpio_pl_ps,
                            uint32_t *gpio_out,
                            uint32_t *gpio_out2,
                            uint32_t *gpio_spi_ps_ctrl,
                            uint32_t *gpio_spi_ps_data){

    // SPI config
    SPI_CONFIG *spi_config = (SPI_CONFIG*)malloc(sizeof(SPI_CONFIG));
    setup_SPI_by_device_name( spi_config, "ads5294" );

    SPI_DATA *spi_data = (SPI_DATA*)malloc(sizeof(SPI_DATA));
    spi_data_alloc(spi_data, 1);

    spi_data_set_LS(spi_data, 
                    0, 
                    0X00,
                    0X01,
                    16 );

    send_spi_data(  gpio_out,
                    gpio_out2,
                    gpio_in,
                    spi_config, 
                    spi_data );

    free(spi_config);

    sleep(1);

    return 0;
}

int socket_sync_ads5294(    SchmidtCAM_Config *cfg, 
                            uint32_t *gpio_flt_ctrl, 
                            uint32_t *gpio_flt_data,
                            uint32_t *gpio_in,
                            uint32_t *gpio_pl_ps,
                            uint32_t *gpio_out,
                            uint32_t *gpio_out2,
                            uint32_t *gpio_spi_ps_ctrl,
                            uint32_t *gpio_spi_ps_data){

    clrbit(*gpio_out, BIT_ADS5294_SYNC);

    printf("(socket_sync_ads5294)> pull-up SYNC.\n");
    setbit(*gpio_out, BIT_ADS5294_SYNC);
    
    usleep(1);

    clrbit(*gpio_out, BIT_ADS5294_SYNC);
    printf("(socket_sync_ads5294)> pull-down SYNC.\n");

    sleep(1);

    return 0;
}


int socket_set_156MHz_clk(  SchmidtCAM_Config *cfg, 
                            uint32_t *gpio_flt_ctrl, 
                            uint32_t *gpio_flt_data,
                            uint32_t *gpio_in,
                            uint32_t *gpio_pl_ps,
                            uint32_t *gpio_out,
                            uint32_t *gpio_out2,
                            uint32_t *gpio_spi_ps_ctrl,
                            uint32_t *gpio_spi_ps_data,
                            int opt ){

    if( 0 == opt ){
        clrbit(*gpio_out, BIT_156MHz_EN);
    }
    else if( 1 == opt ){
        setbit(*gpio_out, BIT_156MHz_EN);
    }
    else {
        printf("(socket_set_156MHz_clk)> opt can only be 0 or 1.\n");
        exit(0);
    }

    sleep(1);

    return 0;
}

int socket_set_afe( SchmidtCAM_Config *cfg, 
                    uint32_t *gpio_flt_ctrl, 
                    uint32_t *gpio_flt_data,
                    uint32_t *gpio_in,
                    uint32_t *gpio_pl_ps,
                    uint32_t *gpio_out,
                    uint32_t *gpio_out2,
                    uint32_t *gpio_spi_ps_ctrl,
                    uint32_t *gpio_spi_ps_data,
                    int opt ){

    if( 0 == opt ){
        clrbit(*gpio_out, BIT_AFE_PD_CTL);
    }
    else if( 1== opt ){
        setbit(*gpio_out, BIT_AFE_PD_CTL);
    }
    else {
        printf("(socket_set_afe)> opt can only be 0 or 1.\n");
        exit(0);
    }

    sleep(1);

    return 0;
}
